// TextPairs.cpp

#include "StdAfx.h"

#include "TextPairs.h"

#include <algorithm>

namespace Common
{
namespace String
{
static const TCHAR kNewLineChar = _T('\n');
static const TCHAR kQuoteChar = _T('\"');

static const wchar_t kBOM = (wchar_t)0xFEFF;

static bool IsSeparatorChar(TCHAR c)
{
  return (c == _T(' ') || c == _T('\t'));
}

static void RemoveCr(CString &s)
{
  s.Replace(_T("\x0D"), _T(""));
}

static CString GetIDString(LPCTSTR srcString, int &finishPos)
{
  CString result;
  bool quotes = false;
  for (finishPos = 0;;)
  {
    TCHAR c = srcString[finishPos];
    if (c == 0)
      break;
    finishPos++;
    bool isSeparatorChar = IsSeparatorChar(c);
    if (c == kNewLineChar || (isSeparatorChar && !quotes)
        || (c == kQuoteChar && quotes))
      break;
    else if (c == kQuoteChar)
      quotes = true;
    else
      result += c;
  }
  result.Trim();
  RemoveCr(result);
  return result;
}

static CString GetValueString(LPCTSTR srcString, int &finishPos)
{
  CString result;
  for (finishPos = 0;;)
  {
    TCHAR c = srcString[finishPos];
    if (c == 0)
      break;
    finishPos++;
    if (c == kNewLineChar)
      break;
    result += c;
  }
  result.Trim();
  RemoveCr(result);
  return result;
}

static bool GetTextPairs(const CString &srcString, std::vector<CTextPair> &pairs)
{
  pairs.clear();
  int pos = 0;
  
  if (srcString.GetLength() > 0)
  {
    if (srcString[0] == kBOM)
      pos++;
  }
  while (pos < srcString.GetLength())
  {
    int finishPos;
	CString id = GetIDString((LPCTSTR)srcString + pos, finishPos);
    pos += finishPos;
    if (id.IsEmpty())
      continue;
    CString value = GetValueString((LPCTSTR)srcString + pos, finishPos);
    pos += finishPos;
    if (!id.IsEmpty())
    {
      CTextPair pair(id, value);
      pairs.push_back(pair);
    }
  }
  return true;
}

static bool ComparePairIDs(const CString &s1, const CString &s2)
  { return 0 < s1.CompareNoCase(s2); }
static bool ComparePairItems(const CTextPair &p1, const CTextPair &p2)
  { return ComparePairIDs(p1.ID, p2.ID); }

//static int ComparePairItems(void *const *a1, void *const *a2, void * /* param */)
 // { return ComparePairItems(**(const CTextPair **)a1, **(const CTextPair **)a2); }

void CPairsStorage::Sort() { std::sort(Pairs.begin(), Pairs.end(), ComparePairItems); }

int CPairsStorage::FindID(const CString &id, int &insertPos)
{
  size_t left = 0, right = Pairs.size();
  while (left != right)
  {
    int mid = (left + right) / 2;
    int compResult = ComparePairIDs(id, Pairs[mid].ID);
    if (compResult == 0)
      return mid;
    if (compResult < 0)
      right = mid;
    else
      left = mid + 1;
  }
  insertPos = left;
  return -1;
}

int CPairsStorage::FindID(const CString &id)
{
  int pos;
  return FindID(id, pos);
}

void CPairsStorage::AddPair(const CTextPair &pair)
{
  int insertPos;
  int pos = FindID(pair.ID, insertPos);
  if (pos >= 0)
    Pairs[pos] = pair;
  else
	  Pairs.insert(Pairs.begin() +insertPos, pair);
}

void CPairsStorage::DeletePair(const CString &id)
{
  int pos = FindID(id);
  if (pos >= 0)
	  Pairs.erase(Pairs.begin() + pos);
}

bool CPairsStorage::GetValue(const CString &id, CString &value)
{
  value.Empty();
  int pos = FindID(id);
  if (pos < 0)
    return false;
  value = Pairs[pos].Value;
  return true;
}

CString CPairsStorage::GetValue(const CString &id)
{
  int pos = FindID(id);
  if (pos < 0)
    return CString();
  return Pairs[pos].Value;
}

bool CPairsStorage::ReadFromString(const CString &text)
{
  bool result = GetTextPairs(text, Pairs);
  if (result)
    Sort();
  else
    Pairs.clear();
  return result;
}

void CPairsStorage::SaveToString(CString &text)
{
  for (size_t i = 0; i < Pairs.size(); i++)
  {
    const CTextPair &pair = Pairs[i];
    bool multiWord = (pair.ID.Find(L' ') >= 0);
    if (multiWord)
      text += _T('\"');
    text += pair.ID;
    if (multiWord)
      text += _T('\"');
    text += _T(' ');
    text += pair.Value;
    text += _T('\x0D');
    text += _T('\n');
  }
}

}
}